﻿using System;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using mki.Backup.Helper;
using mki.Backup.Objects;

namespace mki.Backup.Interfaces
{
	/// <summary>
	/// Database BackupJob Class
	/// </summary>
	internal class DBBackupJob
	{
		#region Methods (1)

		// Public Methods (1) 

		/// <summary>
		/// Runs the job.
		/// </summary>
		/// <returns></returns>
		public static Boolean RunJob(DBBackupElement job, out String backupLocationAndFileName)
		{
			if (String.IsNullOrWhiteSpace(job.ConnectionString)) { throw new ArgumentException("ConnectionString is null or empty!"); }
			if (String.IsNullOrWhiteSpace(job.DatabaseName)) { throw new ArgumentException("DatabaseName is null or empty!"); }
			if (String.IsNullOrWhiteSpace(job.TargetDirectory)) { throw new ArgumentException("TargetDirectory is null or empby!"); }
			// try to create target directory if not exist
			try { if (!Directory.Exists(job.TargetDirectory)) { Directory.CreateDirectory(job.TargetDirectory); } }
			catch (Exception ex) { throw new DirectoryNotFoundException(String.Format("Directory {0} not found: {1}\n{2}", job.TargetDirectory, ex.Message, ex.StackTrace)); }

			backupLocationAndFileName = Path.Combine(job.TargetDirectory, String.Format("{0}_{1}.{2}", job.DatabaseName, DateTime.Now.ToString("yyyyMMdd_HHmm"), job.Method == DBBackupElement.BackupMethod.TransactionLog ? "log" : "bak"));
			try
			{
				var backupData = getConnectionString(job.ConnectionString);

				// set connection
				var serverConnection = new ServerConnection
									   {
										   ServerInstance = backupData.InstanceName,
										   LoginSecure = false,
										   Login = backupData.UserName,
										   Password = backupData.Password,
										   DatabaseName = backupData.Datebase,
										   StatementTimeout = job.StatementTimeout
									   };

				Logging.WriteError("Creating backup of database {0}", backupData.Datebase);

				// aaaaand connect
				var server = new Server(serverConnection);
				try
				{
					server.ConnectionContext.Connect();

					// set backup
					var backup = new Microsoft.SqlServer.Management.Smo.Backup { Action = BackupActionType.Files, Database = backupData.Datebase };
					var backupLocation = Path.Combine(server.BackupDirectory, backupData.Datebase, backupLocationAndFileName);
					backup.Devices.Add(new BackupDeviceItem(backupLocation, DeviceType.File));
					backup.MediaDescription = String.Format("{0} Database {1}-Backup", job.DatabaseName, job.Method);

					switch (job.Method)
					{
						case DBBackupElement.BackupMethod.Differential:
							backup.Incremental = true;
							break;
						case DBBackupElement.BackupMethod.TransactionLog:
							backup.LogTruncation = BackupTruncateLogType.Truncate;
							break;
					}

					// create backup
					backup.SqlBackup(server);

					return true;
				}
				catch (Exception ex)
				{
					Logging.WriteError("Error while backup Database: {0}\n{1}", ex.Message, ex.StackTrace);
					return false;
				}
				finally
				{
					try
					{
						server.ConnectionContext.Disconnect();
					}
					catch (Exception ex)
					{
						Logging.WriteError("Error while disconnect: {0}\n{1}", ex.Message, ex.StackTrace);
					}
				}
			}
			catch (Exception ex)
			{
				Logging.WriteError("while executing db backup job: {0}\n{1}", ex.Message, ex.StackTrace);
				return false;
			}
		}

		/// <summary>
		/// Sets the connection string.
		/// </summary>
		/// <param name="value">The value.</param>
		private static ConnectionString getConnectionString(String value)
		{
			var connectionString = new ConnectionString();
			var regex = new Regex(@"Data Source=(?<server>.*);Initial Catalog=(?<database>.*);User Id=(?<userName>.*);Password=(?<password>.*);", RegexOptions.IgnoreCase);

			var matches = regex.Matches(value);

			if (matches.Count > 0)
			{
				connectionString.InstanceName = matches[0].Groups["server"].Value;
				connectionString.Datebase = matches[0].Groups["database"].Value;
				connectionString.UserName = matches[0].Groups["userName"].Value;
				connectionString.Password = matches[0].Groups["password"].Value;
			}

			return connectionString;
		}

		/// <summary>
		/// ConnectionString object
		/// </summary>
		private struct ConnectionString
		{
			public String InstanceName { get; set; }
			public String Datebase { get; set; }
			public String UserName { get; set; }
			public String Password { get; set; }
		}

		#endregion Methods
	}
}